"""___矩阵操作___"""
import numpy as np

#创建矩阵
a = np.array([[1,2,3,4,5],[6,7,8,9,10]])
#矩阵维度
print(a.ndim)   # 2
# 矩阵行数和列数
print(a.shape)  # (2, 5)
# 矩阵元素个数
print(a.size)  # 10
print(a)

# 创建ndarray
# array：创建数组
# dtype：指定数据类型
a1 = np.array([[1,2,3,4,5],[6,7,8,9,10]],dtype=np.int)
print(type(a1))
# zeros：创建数据全为0
# [[0. 0. 0. 0. 0. 0.]
#  [0. 0. 0. 0. 0. 0.]
#  [0. 0. 0. 0. 0. 0.]]
a2 = np.zeros((3,6))
print(a2)
# ones：创建数据全为1
# [[1. 1. 1. 1. 1.]
#  [1. 1. 1. 1. 1.]]
a3 = np.ones((2,5))
print(a3)
# empty：创建数据接近0  值并非固定
a4 = np.empty((2,4))
print(a4)

# arrange：按指定范围创建数据
a5 = np.arange(0,10,2)  # 0-10之间的数，2为步长 ,默认步长为1
print(a5)    # [0 2 4 6 8]
a6 = np.arange(13)   # 连续数组
print(a6)    # [ 0  1  2  3  4  5  6  7  8  9 10 11 12]
# 创建连续数组后改变数据结构为 4行5列
# [[ 0  1  2  3  4]
#  [ 5  6  7  8  9]
#  [10 11 12 13 14]
#  [15 16 17 18 19]]
a7 = np.arange(20).reshape(4,5)
print(a7)
# linspace：创建线段
a8 = np.linspace(0,20,10)  # 0-20数字等分10份  [ 0.          2.22222222  4.44444444  6.66666667  8.88888889 11.11111111  13.33333333 15.55555556 17.77777778 20.        ]
print(a8)

"""基本操作"""
# 根据索引查看数据
print(a1[0][0])
# 切片
a9 = np.random.randint(1,20)
print(a9)
# 1-10的随机数，3行4列
a10 = np.random.randint(1,20,size=(3,4))
print(a10)

# 获取多维数组的前两行数据
# [[ 1  2  3  4  5]
#  [ 6  7  8  9 10]]
print(a1[0:2])
# 获取多维数组前两行的前两列
# [[1 2]
#  [6 7]]
print(a1[0:2][:,0:2])
# 获取二维数组的前两列
# [[0. 0.]
#  [0. 0.]
#  [0. 0.]]
print(a2[:,0:2])
# 将数组的行倒序
# [[ 6  7  8  9 10]
#  [ 1  2  3  4  5]]
print(a1[::-1])
# 将数组的列倒序
# [[ 5  4  3  2  1]
#  [10  9  8  7  6]]
print(a1[:,::-1])
# 全部倒序
# [[10  9  8  7  6]
#  [ 5  4  3  2  1]]
print(a1[::-1,::-1])

"""变形"""
# 实际应用：图片翻转-图片本质上是三维的array，先将三维变成一维，再通过切片翻转
# reshape() 函数 参数是一个tuple
# 例：将一维数组变形成多维数组===arr.reshape((2,5))
# 例：将多维数组变形成一维数组===arr2 = arr1.reshape((15,))
# 例：arr2.reshape((-1,3)) ===其中-1表示的是根据列自动计算出行

# a11 = np.array(([1,2,3],[4,5,6]),dtype = int)
#
# [[1.+0.j 2.+0.j 3.+0.j]
#  [4.+0.j 5.+0.j 6.+0.j]]
a11 = np.array([[1,2,3],[4,5,6]],dtype = complex)
print(a11)

#数据类型对象
# dt = np.dtype(np.int32)
# print(dt)
# # int8, int16, int32, int64 四种数据类型可以使用字符串 'i1', 'i2','i4','i8' 代替
# dt = np.dtype('i2')
# print(dt)
# #创建结构化数据类型
# dt = np.dtype([('age',np.int8)])
# print(dt)   # [('age', 'i1')]
# # 将数据类型应用于 ndarray对像
# a12 = np.array([1,2,3],dtype = dt)
# print(a12)    # [(1,) (2,) (3,)]
# # 类型字段名可以用于取实际的age列
# print(a12['age'])  #  [1 2 3]

# 定义结构化数据
# student = np.dtype([('name','S20'),('age','i1'),('marks','f4')])
# print(student)   # [('name', 'S20'), ('age', 'i1'), ('marks', '<f4')]
# a = np.array([('abc',15,50),('xyz',18,75)],dtype = student)
print(a)   # [(b'abc', 15, 50.) (b'xyz', 18, 75.)]
# print(a['name'][0])
# ndarray 对象中每个元素的大小，以字节为单位
# print(a.itemsize)
#  ndarray 对象的内存信息
#   C_CONTIGUOUS : True
#   F_CONTIGUOUS : True
#   OWNDATA : True
#   WRITEABLE : True
#   ALIGNED : True
#   WRITEBACKIFCOPY : False
#   UPDATEIFCOPY : False
# print(a.flags)
# ndarray元素的实部
# print(a.real)  # [(b'abc', 15, 50.) (b'xyz', 18, 75.)]
# ndarray元素的虚部
# print(a.imag)  # [(b'', 0, 0.) (b'', 0, 0.)]
# 包含实际数组元素的缓冲区，由于一般通过数组的索引获取元素，所以通常不需要使用这个属性
# print(a.data)  # <memory at 0x0000010797EDE948>
# 调整数组大小
a = np.array([[1,2,3],[4,5,6]])
print(a.shape)  # 形状：2行3列 (2, 3)
print(a.ndim)   # 维度：2
#调整数组形状(大小)
# a.shape = (3,2)
# print(a)  # [[1 2]
#           #  [3 4]
#            # [5 6]]
b = a.reshape(3,2)
print(a)  # [[1 2 3]
          # [4 5 6]]
print(b)  # [[1 2]
            #[3 4]
            #[5 6]]
# 以字节的形式返回数组中每一个元素的大小。
print(a.itemsize)  # 默认dtpye为 int32，一个字节长度为8，32/8=4

"""创建数组"""
# numpy.empty(shape, dtype = float, order = 'C')
# shape：数组形状， dtype：数据类型，可选
# order：有"C"和"F"两个选项,分别代表，行优先和列优先，在计算机内存中的存储元素的顺序。
x = np.empty((2,3),dtype=int)
# 数组元素为随机值，因为它们未初始化。
#[[    512       0        0]
# [6553710       0  4456540]]
print(x)

# numpy.zeros
# 创建指定大小的数组，数组元素以 0 来填充：
# numpy.zeros(shape, dtype = float, order = 'C')
c = np.zeros((2,3),dtype = str)
# [['' '' '']
#  ['' '' '']]
print(c)

c = np.zeros([2,3],dtype=int)
#[[0 0 0]
# [0 0 0]]
print(c)

c = np.zeros(5)  # 默认数据类型为浮点数
print(c)  # [0. 0. 0. 0. 0.]

# 自定义类型
z = np.zeros((2,2), dtype = [('x', 'i4'), ('y', 'i4')])
# [[(0, 0) (0, 0)]
#  [(0, 0) (0, 0)]]
print(z)

# numpy.ones
# 创建指定形状的数组，数组元素以 1 来填充：
# numpy.ones(shape, dtype = None, order = 'C')
# 'C' 用于 C 的行数组，或者 'F' 用于 FORTRAN 的列数组
x = np.ones(5)
print(x)   # [1. 1. 1. 1. 1.]

# 自定义类型
# [[1 1]
#  [1 1]]
x = np.ones([2,2], dtype = int)
print(x)

# numpy.asarray
# numpy.asarray 类似 numpy.array，但 numpy.asarray 参数只有三个，比 numpy.array 少两个。
# numpy.asarray(a, dtype = None, order = None)
# 任意形式的输入参数，可以是，列表, 列表的元组, 元组, 元组的元组, 元组的列表，多维数组

# 将列表转为ndarray
x = [1,2,3]
a = np.asarray(x)
print(a)   # [1 2 3]
#将元组转为ndarray
x = (1,2,3)
a = np.asarray(x)
print(a)    # [1 2 3]
a = np.asarray(x,dtype=float)
print(a)    # [1. 2. 3.]
#将元组列表转为ndarray,列表中的元组元素个数需一致
x = [(1,2,3),(4,5,6)]
a = np.asarray(x)
print(a)  # [(1, 2, 3) (4, 5)] /  [[1 2 3]
                                 # [4 5 6]]
# numpy.frombuffer =====================================================
# numpy.frombuffer 用于实现动态数组。
# numpy.frombuffer 接受 buffer 输入参数，以流的形式读入转化成 ndarray 对象。
# numpy.frombuffer(buffer, dtype = float, count = -1, offset = 0)
# 注意：buffer 是字符串的时候，Python3 默认 str 是 Unicode 类型，所以要转成 bytestring 在原 str 前加上 b。
# buffer	可以是任意对象，会以流的形式读入。
# dtype	返回数组的数据类型，可选
# count	读取的数据数量，默认为-1，读取所有数据。
# offset	读取的起始位置，默认为0。

#：buffer 是字符串的时候，Python3 默认 str 是 Unicode 类型，所以要转成 bytestring 在原 str 前加上 b
s = b'hello world'
a = np.frombuffer(s,dtype='S1')
print(a)  # [b'h' b'e' b'l' b'l' b'o' b' ' b'w' b'o' b'r' b'l' b'd']

# numpy.fromiter  ======================================================
# numpy.fromiter 方法从可迭代对象中建立 ndarray 对象，返回一维数组。
# numpy.fromiter(iterable, dtype, count=-1)
# iterable	可迭代对象
# dtype	返回数组的数据类型
# count	读取的数据数量，默认为-1，读取所有数据

list=range(10)
a = iter(list)
# x = np.fromiter(a,dtype=float,count=5)
# print(x)    # [0. 1. 2. 3. 4.]
x = np.fromiter(a,dtype=float)
print(x)    # [0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]


# numpy.arange  ==============================================================
# numpy 包中的使用 arange 函数创建数值范围并返回 ndarray 对象，函数格式如下：
# numpy.arange(start, stop, step, dtype)
# 根据 start 与 stop 指定的范围以及 step 设定的步长，生成一个 ndarray。
# start	起始值，默认为0
# stop	终止值（不包含）
# step	步长，默认为1
# dtype	返回ndarray的数据类型，如果没有提供，则会使用输入数据的类型。

# 生成从0到5的数组
x = np.arange(5)
print(x)    # [0 1 2 3 4]

# 设置数据类型
x = np.arange(5,dtype=float)
print(x)   # [0. 1. 2. 3. 4.]

# 设置起始值、终止值及步长
x = np.arange(10,20,step=2)
print(x)   # [10 12 14 16 18]

# numpy.linspace 等差数列 ==============================================================
# numpy.linspace 函数用于创建一个一维数组，数组是一个等差数列构成的，格式如下：
# np.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)
# start	序列的起始值
# stop	序列的终止值，如果endpoint为true，该值包含于数列中
# num	要生成的等步长的样本数量，默认为50
# endpoint	该值为 true 时，数列中中包含stop值，反之不包含，默认是True。
# retstep	如果为 True 时，生成的数组中会显示间距，反之不显示。
# dtype	ndarray 的数据类型

# 设置起始点1，终止点10，数列个数为200
a = np.linspace(1,10,10)
print(a)  # [ 1.  2.  3.  4.  5.  6.  7.  8.  9. 10.]
# 设成全是1的等差数列
a = np.linspace(1,1,10)
print(a)  # [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
# 不包含终止值
a = np.linspace(10,20,5,endpoint=False)
print(a)   # [10. 12. 14. 16. 18.]
# 设置间距
a = np.linspace(1,10,10,retstep=True)
print(a)  # (array([ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.]), 1.0)
b = np.linspace(1,10,10).reshape(5,2)
# [[ 1.  2.]
#  [ 3.  4.]
#  [ 5.  6.]
#  [ 7.  8.]
#  [ 9. 10.]]
print(b)

# numpy.logspace  等比数列  =========================================================
# numpy.logspace 函数用于创建一个于等比数列。格式如下：
# np.logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None)
# start	序列的起始值为：base ** start
# stop	序列的终止值为：base ** stop。如果endpoint为true，该值包含于数列中
# num	要生成的等步长的样本数量，默认为50
# endpoint	该值为 true 时，数列中中包含stop值，反之不包含，默认是True。
# base	对数 log 的底数。
# dtype	ndarray 的数据类型

# 默认 base为10
# [ 10.          12.91549665  16.68100537  21.5443469   27.82559402
#   35.93813664  46.41588834  59.94842503  77.42636827 100.        ]
a = np.logspace(1.0,2.0,num=10)
print(a)
# 设置底数为2
# [  1.   2.   4.   8.  16.  32.  64. 128. 256. 512.]
a = np.logspace(0,9,10,base=2)
print(a)

"""NumPy 切片和索引  """
# ---------------------------------------------------------
#  ndarray对象的内容可以通过索引或切片来访问和修改，与 Python 中 list 的切片操作一样。
#  ndarray 数组可以基于 0 - n 的下标进行索引，切片对象可以通过内置的 slice 函数，
# 并设置 start, stop 及 step 参数进行，从原数组中切割出一个新数组。

# 从索引 2 开始到索引 7 停止，间隔为2
# [2 4 6]
a = np.arange(10)
s = slice(2,7,2)
print(a[s])
# 可以通过冒号分隔切片参数 start:stop:step 来进行切片操作：
print(a[2:9:2])   # [2 4 6 8]
# 冒号 : 的解释：如果只放置一个参数，如 [2]，将返回与该索引相对应的单个元素。
# 如果为 [2:]，表示从该索引开始以后的所有项都将被提取。
# 如果使用了两个参数，如 [2:7]，那么则提取两个索引(不包括停止索引)之间的项。

# 切片还可以包括省略号 …，来使选择元组的长度与数组的维度相同。
# 如果在行位置使用省略号，它将返回包含行中元素的 ndarray。
a = np.array([[1,2,3],[3,4,5],[4,5,6]])
print(a)   # [[1 2 3]
            # [3 4 5]
            # [4 5 6]]
print(a[...,1])   # 获取第二列元素  [2 4 5]
print(a[1,...])   # 获取第二行元素  [3 4 5]
print(a[...,1:])  # 获取第二列及以后列的元素

"""高级索引"""
a = np.array([[1,2],[3,4],[5,6]])
# 以下实例获取数组中(0,0)，(1,1)和(2,0)位置处的元素。
print(a[[0,1,2],[0,1,0]])
# 获取4*3矩阵中的四个角的元素，行索引是 [0,0],[3,3]]，列索引是 [0,2],[0,2]
a = np.array([[0,1,2],[3,4,5],[6,7,8],[9,10,11]])
# 代表取得是 a[0][0],a[0][2],a[3][0],a[3][2]
rows = np.array([[0,0],[3,3]])
cols = np.array([[0,2],[0,2]])
print(a[0][0])
print(a[rows,cols])  #[[ 0  2]
                     # [ 9 11]]

# 布尔索引
# 获取大于5的元素
print(a[a>5])   # [ 6  7  8  9 10 11]

# 取~（补运算符）来过滤NaN
a = np.array([np.nan,1,2,np.nan,4,5])
print(a[~np.isnan(a)])  # [1. 2. 4. 5.]
# 过滤非复数
a = np.array([1,1+3j,1-2j,5])
print(a[np.iscomplex(a)])   # [1.+3.j 1.-2.j]

# 花式索引 -- 利用整数数组进行索引
# 1、传入顺序索引数组
a = np.arange(32).reshape(8,4)
print(a)
print('-------')
# -------
# [[16 17 18 19]
#  [ 8  9 10 11]
#  [ 4  5  6  7]
#  [28 29 30 31]]
print(a[[4,2,1,7]])
# 传入倒序索引数组
print(a[[-4,-2,-1,-7]])
print('-------')
# 传入多个索引数组 要使用np.ix_
print(a[np.ix_([1,5,7,2],[0,3,1,2])])

"""广播"""
# 广播(Broadcast)是 numpy 对不同形状(shape)的数组进行数值计算的方式，
# 对数组的算术运算通常在相应的元素上进行
# 如果两个数组 a 和 b 形状相同，即满足 a.shape == b.shape，那么 a*b 的结果就是 a 与 b 数组对应位相乘。
# 这要求维数相同，且各维度的长度相同。

# 广播的规则:
# 让所有输入数组都向其中形状最长的数组看齐，形状中不足的部分都通过在前面加 1 补齐。
# 输出数组的形状是输入数组形状的各个维度上的最大值。
# 如果输入数组的某个维度和输出数组的对应维度的长度相同或者其长度为 1 时，这个数组能够用来计算，否则出错。
# 当输入数组的某个维度的长度为 1 时，沿着此维度运算时都用此维度上的第一组值。

# 简单理解：对两个数组，分别比较他们的每一个维度（若其中一个数组没有当前维度则忽略），满足：
# 数组拥有相同形状。
# 当前维度的值相等。
# 当前维度的值有一个是 1。
# 若条件不满足，抛出 "ValueError: frames are not aligned" 异常。

# 一维矩阵相乘
a = np.array([1,2,3,4])
b = np.array([10,20,30,40])
c = np.array([[1,2,3,4],[5,6,7,8]])
d = np.array([[1,2,3,4],[4,5,6,7],[7,8,9,10]])
print(a * b)  # [ 10  40  90 160]
print('--------')
# [[ 1  4  9 16]
#  [ 5 12 21 32]]
print(a*c)
# [[ 1  4  9 16]
#  [ 4 10 18 28]
#  [ 7 16 27 40]]
print(a*d)
# [[ 10  40  90 160]
#  [ 50 120 210 320]]
# 列复制为原来的1倍，行复制为原来的3倍
# [[1 2 3 4]
#  [5 6 7 8]
#  [1 2 3 4]
#  [5 6 7 8]
#  [1 2 3 4]
#  [5 6 7 8]]
c1 = np.tile(c,(3,1))
print(c1)
# 多维矩阵和一维矩阵相加 ，会触发广播机制
# [[ 1  2  3]
#  [11 12 13]
#  [21 22 23]
#  [31 32 33]]
a = np.array([[ 0, 0, 0],
           [10,10,10],
           [20,20,20],
           [30,30,30]])
b = np.array([1,2,3])
print(a + b)
# 4x3 的二维数组与长为 3 的一维数组相加，等效于把数组 b 在二维上重复 4 次再运算：
# [[ 1  2  3]
#  [11 12 13]
#  [21 22 23]
#  [31 32 33]]
a = np.array([[ 0, 0, 0],
           [10,10,10],
           [20,20,20],
           [30,30,30]])
b = np.array([1,2,3])
bb = np.tile(b, (4, 1))  # 重复 b 的各个维度
print(a + bb)

a = np.arange(6).reshape(2,3)
print("原始数组是：")
# [[0 1 2]
#  [3 4 5]]
print(a)
print('\n')
print('迭代输出元素：')
for x in np.nditer(a):
    print(x,end=', ')
print('\n')
# 以上实例不是使用标准 C 或者 Fortran 顺序，选择的顺序是和数组内存布局一致的，
# 这样做是为了提升访问的效率，默认是行序优先（row-major order，或者说是 C-order）。
print(a.T)
for x in np.nditer(a.T):
    print(x,end=", ")
print('\n')     # 0, 1, 2, 3, 4, 5,

# 以行优先
for x in np.nditer(a.T.copy(order='C')):
    print(x,end=', ')
print('\n')     # 0, 3, 1, 4, 2, 5,

try:
    # 以列优先
    for x in np.nditer(a.T.copy(order='F')):
        print(x,end=', ')
except Exception as reason :
    print('异常是：'+str(reason))
print('\n')

# 修改数组中元素的值
# nditer 对象有另一个可选参数 op_flags。 默认情况下，nditer 将视待迭代遍历的数组为只读对象（read-only），
# 为了在遍历数组的同时，实现对数组元素值得修改，必须指定 read-write 或者 write-only 的模式。

a = np.arange(0,60,5)
a = a.reshape(3,4)
print ('原始数组是：')
print (a)
print ('\n')
# for x in np.nditer(a, op_flags=['readwrite']):
try:
    for x in np.nditer(a,op_flags=['writeonly']):
        x[...]=2*x
except Exception as reason :
    print('异常是：'+str(reason))
print ('修改后的数组是：')
print (a)

# 使用外部循环
# nditer类的构造器拥有flags参数，它可以接受下列值：
# c_index	可以跟踪 C 顺序的索引
# f_index	可以跟踪 Fortran 顺序的索引
# multi-index	每次迭代可以跟踪一种索引类型
# external_loop	给出的值是具有多个值的一维数组，而不是零维数组

# 在下面的实例中，迭代器遍历对应于每列，并组合为一维数组。
a = np.arange(0,60,5)
a = a.reshape(3,4)
print ('原始数组是：')
print (a)
print ('\n')
print ('修改后的数组是：')
for x in np.nditer(a, flags =  ['external_loop'], order =  'F'):
   print (x, end=", " )
print ('\n')
# 广播迭代
# 如果两个数组是可广播的，nditer 组合对象能够同时迭代它们。
# 假设数组 a 的维度为 3X4，数组 b 的维度为 1X4 ，则使用以下迭代器（数组 b 被广播到 a 的大小）
a = np.arange(0,60,5)
a = a.reshape(3,4)
print('第一个数组为：')
print(a)
print('\n')
print('第二个数组为：')
b = np.array([1,  2,  3,  4], dtype =  int)
print(b)
print('\n')
print('修改后的数组为：')
for x,y in np.nditer([a,b]):
    print ("%d:%d"  %  (x,y), end=", " )


